package com.puzzle

import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Rect

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.IntOffset
import androidx.core.graphics.createBitmap

import java.util.Random

import kotlin.math.abs
import kotlin.math.sqrt

// 游戏
class Game {
    // 难度
    enum class Difficulty(private val _value: Int) {
        EASY(9),    // 简单
        MEDIUM(25), // 中等
        HARD(36);   // 困难

        /**
         * 获取枚举值。
         * @return 枚举值
         */
        fun value(): Int {
            return _value
        }
    }

    // 状态
    enum class Status {
        CREATING,   // 正在创建游戏物体
        END,        // 已结束
        PLAYING,    // 游戏中
        WON         // 游戏胜利
    }

    private var mDifficulty: Difficulty                 // 难度
    private val mEmptyAreaRect: Rect                    // 空白区域矩形
    private var mEmptyPoint: IntOffset                  // 空点，即没有图的那个点
    private lateinit var mImage: Bitmap                 // 图片
    private var mPicHasBorder: Boolean                  // 图片控件是否有边框标志
    private lateinit var mPictures:
        SnapshotStateList<PictureCtrl?>                 // 图片控件列表
    private var mResources: Resources                   // 资源
    private val mRandom: Random                         // 随机数生成器
    private lateinit var mStatus: MutableState<Status>  // 状态

    /**
     * 构造方法。
     * @param resources 资源
     */
    constructor(resources: Resources) {
        val gameBoardWidth = resources.displayMetrics.widthPixels
        val gameBoardHeight = resources.displayMetrics.heightPixels

        // 初始化成员
        mDifficulty = Difficulty.EASY
        mEmptyPoint = IntOffset.Zero
        mPicHasBorder = true
        mRandom = Random()
        mResources = resources

        // 设置空白区域的位置和尺寸
        val diffBaseMedium = sqrt(Difficulty.MEDIUM.value().toDouble()).toInt()
        val diffBaseHard = sqrt(Difficulty.HARD.value().toDouble()).toInt()
        var emptyAreaSize = gameBoardHeight
        do {
            emptyAreaSize--
        } while (emptyAreaSize % diffBaseMedium != 0 || emptyAreaSize % diffBaseHard != 0)
        val emptyAreaLeft = (gameBoardWidth - emptyAreaSize) / 2
        val emptyAreaTop = (gameBoardHeight - emptyAreaSize) / 2
        mEmptyAreaRect = Rect(emptyAreaLeft, emptyAreaTop, emptyAreaLeft + emptyAreaSize, emptyAreaTop + emptyAreaSize)
    }

    /**
     * 创建所有图片控件。
     */
    @Composable
    private fun CreatePictures() {
        var left: Int
        var top: Int                                                    // 图片控件位置
        val baseCount = sqrt(mDifficulty.value().toDouble()).toInt()    // 一行（列）的图片控件数
        val size = mEmptyAreaRect.width() / baseCount                   // 图片控件尺寸
        var j = 0                                                       // 换行标志，0为第一行，1为第二行，依次类推

        // 删除所有图片控件
        this.deletePictures()

        // 创建所有图片控件
        for (i in 0..< mDifficulty.value()) {
            if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一行
                j++
            }

            // 计算左上角坐标
            left = mEmptyAreaRect.left + size * (i % baseCount)
            top = mEmptyAreaRect.top + size * j

            // 创建图片控件
            val pictureCtrl = PictureCtrl(movePicture, mImage, left, top, size)
            pictureCtrl.Init(mPicHasBorder)
            mPictures.add(pictureCtrl)
        }
    }

    /**
     * 删除所有图片控件。
     */
    private fun deletePictures() {
        mPictures.clear()
    }

    /**
     * 获取/设置游戏难度。
     */
    var difficulty: Difficulty
        get() = mDifficulty
        set(value) {
            // 设置难度
            mDifficulty = value

            // 将状态设置为已结束
            mStatus.value = Status.END
        }

    /**
     * 创建游戏区域。
     */
    @Composable
    fun GameBoard() {
        // 初始化成员
        val gameBoardWidth = mResources.displayMetrics.widthPixels
        val gameBoardHeight = mResources.displayMetrics.heightPixels
        mPictures = remember {
            mutableStateListOf<PictureCtrl?>()
        }
        mImage = remember {
            mImage = createBitmap(gameBoardWidth, gameBoardHeight)
            val bitmap = BitmapFactory.decodeResource(mResources, R.mipmap.pic)
            Canvas(mImage).drawBitmap(
                bitmap,
                Rect(0, 0, bitmap.width, bitmap.height),
                Rect(0, 0, gameBoardWidth, gameBoardHeight),
                null
            )
            mImage
        }
        mStatus = remember {
            mutableStateOf(Status.END)
        }
        if (mStatus.value == Status.END || mStatus.value == Status.CREATING) {
            this.CreatePictures()
        }

        // 创建游戏区域
        Canvas(Modifier.fillMaxSize()) {
            this.drawImage(mImage.asImageBitmap())
            this.drawRect(Const.EMPTY_AREA_COLOR,
                Offset(mEmptyAreaRect.left.toFloat(), mEmptyAreaRect.top.toFloat()),
                    Size(mEmptyAreaRect.width().toFloat(), mEmptyAreaRect.height().toFloat()))
        }

        // 显示所有图片控件
        mPictures.forEach {
            it?.Greeting()
        }
    }

    /**
     * 游戏结束。
     */
    private fun gameOver() {
        // 将状态设置为已结束
        mStatus.value = Status.WON
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private fun isWon(): Boolean {
        var result = false
        for (i in 0..< mDifficulty.value()) {
            result = mPictures[i]!!.isOnOriginalPosition()
            if (!result) {
                break
            }
        }
        return result
    }

    /**
     * 移动图片控件。
     * @param pictureCtrl   图片控件
     */
    private val movePicture = fun(pictureCtrl: PictureCtrl) {
        if (mStatus.value != Status.PLAYING) {
            return
        }

        val position = pictureCtrl.position.copy()

        // 如果要移动的图片控件与空点相邻，则移动
        if (position.y == mEmptyPoint.y && abs(position.x - mEmptyPoint.x) == pictureCtrl.size ||
            position.x == mEmptyPoint.x && abs(position.y - mEmptyPoint.y) == pictureCtrl.size) {
            pictureCtrl.position = mEmptyPoint
            mEmptyPoint = position

            // 判断是否胜利
            if (this.isWon()) {
                this.gameOver()
            }
        }
    }

    /**
     * 获取/设置图片控件是否有边框。
     */
    var pictureHasBorder : Boolean
        get() = mPicHasBorder
        set(value) {
            mPicHasBorder = value

            // 设置所有图片控件的边框
            for (i in 0 ..< mDifficulty.value()) {
                mPictures[i]!!.border = value
            }
        }

    /**
     * 设置图片。
     * @param image 图片
     */
    fun setPicture(image: Bitmap) {
        // 将图片画到游戏区域上
        Canvas(mImage).drawBitmap(
            image, 
            Rect(0, 0, image.getWidth(), image.getHeight()),
            Rect(0, 0, mImage.getWidth(), mImage.getHeight()),
            null
        )

        // 将状态设置为已结束
        mStatus.value = Status.END
    }

    /**
     * 游戏开始。
     */
    fun start() {
        // 如果不在游戏中，则右下角图片控件右移
        if (mStatus.value != Status.PLAYING) {
            val baseCount = sqrt(mDifficulty.value().toDouble()).toInt()
            val size = mPictures[0]!!.size
            mEmptyPoint = IntOffset(
                mEmptyAreaRect.left + size * baseCount,
                mEmptyAreaRect.top + size * (baseCount - 1)
            )
            val position = mPictures[mDifficulty.value() - 1]!!.position.copy()
            mPictures[mDifficulty.value() - 1]!!.position = mEmptyPoint
            mEmptyPoint = position
        }

        mStatus.value = Status.PLAYING

        // 乱序其它图片控件的位置
        var index1: Int
        var index2: Int
        repeat(mDifficulty.value() - 1) {
            do {
                index1 = mRandom.nextInt(mDifficulty.value() - 1)
                index2 = mRandom.nextInt(mDifficulty.value() - 1)
            } while (index1 == index2)
            this.swapPictures(index1, index2)
        }
    }

    /**
     * 获取状态。
     */
    val status: Status
        get() = mStatus.value

    /**
     * 交换两个图片控件的位置。
     * @param index1    图片控件1的索引
     * @param index2    图片控件2的索引
     */
    private fun swapPictures(index1: Int, index2: Int) {
        val position1 = mPictures[index1]!!.position.copy()
        val position2 = mPictures[index2]!!.position.copy()
        mPictures[index1]!!.position = position2
        mPictures[index2]!!.position = position1
    }
}
